package editor.modelo;

import java.awt.geom.Point2D;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.OutputStreamWriter;
import java.util.ArrayList;

import java.util.LinkedList;


import editor.modelo.Portal.Clase;


public class ModelUtilities {



	/**
	 * Este devuelve los nombres reales, usar para editar
	 * 
	 * @param workingDir
	 *            que edificio
	 * @param jc
	 *            determina que tipo de objetos se crean (editor o node, etc)
	 * @return
	 */
	public static ArrayList<Floor> loadBuilding(File workingDir,
			JSONConverter jc) {
		ArrayList<Floor> lf = new ArrayList<Floor>();
		LinkedList<Portal> portalesExternosHorizontal = new LinkedList<Portal>();
		LinkedList<Portal> portalesExternosVertical = new LinkedList<Portal>();
		LinkedList<Integer> portalesExternosVerticalHeight = new LinkedList<Integer>();
		String[] archivos = workingDir.list(new FilenameFilter() {
			@Override
			public boolean accept(File dir, String name) {
				return name.endsWith(".json");
			}
		});
		for (String archivo : archivos) {
			Floor f = jc.fromJSONFile(workingDir.getPath() + "/" + archivo);
			// f.setbuildingName(workingDir.getName());
			lf.add(f);
		}
		for (Floor f : lf) {
			for (Portal p : f.getPortales()) {
				if (p.getClase() != Clase.intra_floor) {
					// level*2 +/-1 me permite olvidarme de si sube o baja. Si
					// tienen el mismo level se linkean.
					// el +10 es para que no sea 0
					//////////////NO existe mas/////////////////
					if (p.getTipo() == Portal.Tipo.ustair) {
						portalesExternosVertical.add(p);
						portalesExternosVerticalHeight.add((f.getLevel()) * 2 + 1);
					} else if (p.getTipo() == Portal.Tipo.dstair) {
						portalesExternosVertical.add(p);
						portalesExternosVerticalHeight
								.add((f.getLevel()) * 2 - 1);
			//////////////NO existe mas/////////////////
					} else {
						portalesExternosHorizontal.add(p);
					}
					continue;
				}
			}
		}
		ModelUtilities.linkPortalesExternosHorizontales(portalesExternosHorizontal);
		ModelUtilities.linkPortalesExternosVerticales(portalesExternosVertical,
				portalesExternosVerticalHeight);
		return lf;
	}

	private static void linkPortalesExternosVerticales(
			LinkedList<Portal> portExt, LinkedList<Integer> portHeight) {
		@SuppressWarnings("unchecked")
		LinkedList<Portal> aux = (LinkedList<Portal>) portExt.clone();
		boolean changed;

		while (aux.size() > 1) {
			changed = false;
			Portal p = aux.get(0);
			for (int j = 1; j < aux.size(); j++) {
				Portal p2 = aux.get(j);
				if (portHeight.get(0) == portHeight.get(j)
						&& matchVerticalPortals(p, p2)) {
					p.setTarget(p2.getSource());
					p2.setTarget(p.getSource());
					p.setPtoTarget(p2.getPtoSource());
					p2.setPtoTarget(p.getPtoSource());
					aux.remove(j); // p2
					aux.remove(0); // p
					portHeight.remove(j);
					portHeight.remove(0);// ojo con el orden en que se elimina
					// si se elimina al reves, como j>0, hay q eliminar j-1

					changed = true;
					break;
				}
			}
			if (!changed) {
				System.out
						.println("Hubo problemas al linkear portales externos verticales. Posiblemente no estan correspondidos");
				break;
			}
		}
	}

	/*
	 * Los portales matchean si coinciden en 1 de sus puntos (para considerar
	 * escaleras en U, rectas o en L).
	 */
	private static boolean matchVerticalPortals(Portal p1, Portal p2) {
		Point2D p11, p12, p21, p22;
		p11 = p1.getPtoSource();
		p12 = p1.getSource().getNextPoint(p11);
		p21 = p2.getPtoSource();
		p22 = p2.getSource().getNextPoint(p21);

		if ((p11.distanceSq(p21) < 100 && p12.distanceSq(p22) < 100)) {
			System.out.println("Linking " + p1.getSource().getNombreCompleto()
					+ " con " + p2.getSource().getNombreCompleto());
			// p21.setLocation(p11);
			p21.setLocation(p11);
			p22.setLocation(p12); // los pego para que se vea pipicucu
			return true;

		}
		if ((p11.distanceSq(p22) < 100 && p12.distanceSq(p21) < 100)) {
			System.out.println("Linking " + p1.getSource().getNombreCompleto()
					+ " con " + p2.getSource().getNombreCompleto());
			p21.setLocation(p12);
			p22.setLocation(p11);
			return true;
		}
		System.out.println("No se puede linkear "
				+ p1.getSource().getNombreCompleto() + " con "
				+ p2.getSource().getNombreCompleto());

		return false;
	}

	public static void linkPortalesExternosHorizontales(
			LinkedList<Portal> portalesExt) {
		@SuppressWarnings("unchecked")
		LinkedList<Portal> auxiliar = (LinkedList<Portal>) portalesExt.clone();
		boolean changed;
		while (auxiliar.size() > 0) {
			changed = false;
			Portal p = auxiliar.get(0);
			for (Portal p2 : auxiliar) {
				if (p != p2 && matchHorizontalPortals(p, p2)) {
					p.setTarget(p2.getSource());
					p2.setTarget(p.getSource());
					p.setPtoTarget(p2.getPtoSource());
					p2.setPtoTarget(p.getPtoSource());
					auxiliar.remove(p2);
					auxiliar.remove(p);
					changed = true;
					break;
				}
			}
			if (!changed) {
				System.out.println("Hubo problemas al portales." +
						" Posiblemente no estan correspondidos, o muy distanciados");
				if(auxiliar.size()>0) auxiliar.remove(0);
			}
		}
	}
	public static void linkInternalNonStairPortals(Floor f) {
		System.out.println("Linking internal portals...");

		LinkedList<Portal> unlinkedPortals = new LinkedList<Portal>();
		for (Portal p : f.getPortales()) {
			if (p.getClase() == Clase.intra_floor
					&& (p.getTipo() != Portal.Tipo.ustair && p.getTipo() != Portal.Tipo.ustair)) {

				if (p.getTarget() == null)
					unlinkedPortals.add(p);

			}
		}
		boolean changed;
		while (unlinkedPortals.size() > 0) {
			changed = false;
			Portal p = unlinkedPortals.get(0);
			for (Portal p2 : unlinkedPortals) {
				if (p != p2 && matchHorizontalPortals(p, p2)) {
					p.setTarget(p2.getSource());
					p2.setTarget(p.getSource());
					p.setPtoTarget(p2.getPtoSource());
					p2.setPtoTarget(p.getPtoSource());
					unlinkedPortals.remove(p2);
					unlinkedPortals.remove(p);
					f.getPortales().remove(p2); //remuevo alguo de los dos porque estan repetidos
					changed = true;
					break;
				}
			}
			if (!changed) {
				System.out
						.println("Hubo problemas al portales. Posiblemente no estan correspondidos, o muy distanciados");
				break;
			}
		}
		

	}


	/**
	 * Los portales matchean (se linkean) si coinciden los puntos que los unen
	 * 
	 * @param p1
	 * @param p2
	 * @return
	 */
	public static boolean matchHorizontalPortals(Portal p1, Portal p2) {
		Point2D p11, p12, p21, p22;
		p11 = p1.getPtoSource();
		p12 = p1.getSource().getNextPoint(p11);
		p21 = p2.getPtoSource();
		p22 = p2.getSource().getNextPoint(p21);

		if ((p11.distanceSq(p21) < 200 && p12.distanceSq(p22) < 200)
				|| (p11.distanceSq(p22) < 200 && p12.distanceSq(p21) < 200)) {
			return true;
		}
		return false;
	}

	public static void saveSpaceList(LinkedList<String> aulas, File workingDir) {
		FileOutputStream fos;
		OutputStreamWriter out;
		try {
			File f = new File(workingDir.getCanonicalPath() + "/aulas.txt");
			System.out.println(f.getAbsolutePath());
			f.createNewFile();
			fos = new FileOutputStream(f, false);

			out = new OutputStreamWriter(fos, "UTF-8");
			out.write('[');
			int i = aulas.size() - 1;
			for (String s : aulas) {
				out.write("'" + s + "'");
				if (i-- > 0)
					out.write(", ");
			}
			out.write(']');

			out.close();

		} catch (Exception e) {

			e.printStackTrace();
		}

	}

}
